package keystore;

import java.io.*;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class Node extends UnicastRemoteObject implements INode {
	private static final long serialVersionUID = 1L;
	private int nodeNr = 0;
	private static final int MAX_LEVEL = 6;
	
	// search key
	int key     = 0;
	String record = null;
	
	// key range
	long key_min = 0;
	long key_max = 0;
	
	INode left  = null;
	INode right = null;
	
	int level = 0;
	
	private HashMap<Integer, String> store = null;
	
	
	public Node() throws RemoteException {
		
	}
	
	
	public Node(int nr) throws RemoteException {
		super();
		this.nodeNr = nr;
		String port = Integer.toString(Registry.REGISTRY_PORT);
		try {
			System.setSecurityManager( new RMISecurityManager() );
			Node nodeR = new Node();
			Naming.rebind("//localhost:" + port + "/" + INode.SERVICENAME, nodeR);
			System.out.println("Bound RMI object in local registry at port " + port);
		} catch (Exception ex) {
			ex.printStackTrace();
		}			
	}	
	
	
	public Node(INode left, INode right) throws RemoteException {
		super();
		this.left = left;
		this.right = right;		
	}
	
	
	public Node(INode left, INode right, int key, String record, int minKey, int maxKey, int level) throws RemoteException {
		super();
		this.left = left;
		this.right = right;
		this.sendNodeParameters(key, record, minKey, maxKey, level);
	}
	
	

	

	@Override
	public synchronized void sendNodeParameters(int key, String record, long minKey, long maxKey, int level)
			throws RemoteException {

		System.out.println("*************** NodeNr: " +this.nodeNr);
		
		if (level > 0 && level < MAX_LEVEL) {
			setKey(key);
			setRecord(record);
			setKey_min(minKey);
			setKey_max(maxKey);
			setLevel(level);
			this.callChild();
		}
		else if (level == MAX_LEVEL) {
			if (this.store == null) {
				this.store = new HashMap<Integer, String>();
			}
		
			this.store.put(key, record);

			Iterator entries = store.entrySet().iterator();
			while (entries.hasNext()) {
			    Map.Entry<Integer, String> entry = (Map.Entry) entries.next();
			    Integer key_ = (Integer)entry.getKey();
			    String value = (String)entry.getValue();
			    System.out.println(" -------- Key = " + key_ + ", Value = " + value);
			}			
			
			System.out.println("min = " +minKey +" - max = " +maxKey +" - searchKey = " +key);
			
			// connecting back to client
			String port = Integer.toString(Registry.REGISTRY_PORT);
			try {
				IClient obj = (IClient) Naming.lookup("//localhost" + ":" + port + "/" + IClient.SERVICENAME);
				obj.operationFinished(true);
			} catch (MalformedURLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NotBoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		else {
			// go home
			System.err.println("problem occured, index counting fail...");
		}			
				
	}
	
	
	public void callChild() throws RemoteException {
		// same as (key_min+key_max)/2, avoids overflow.
		long mid = this.key_min + (this.key_max - this.key_min) / 2;		
		
		if (this.key < mid) {
			// call the left node
			if (this.left != null) {
				this.left.sendNodeParameters(this.key, this.record, this.key_min, mid, ++this.level);				
			}
			else {
				System.out.println("Null-Pointer!");
			}			
		}
		else if (this.key > mid) {
			// call the right node
			if (this.right != null) {
				this.right.sendNodeParameters(this.key, this.record, mid, this.key_max, ++this.level);				
			}
			else {
				System.out.println("Null-Pointer!");
			}			
		}
		else {
			// go home
			System.out.println("problem occured, search key is key from one leaf...");
		}		
	}
	
	
	public void setChildren(INode left, INode right) {
		this.left = left;
		this.right = right;		
	}


	public long getKey_min() {
		return key_min;
	}
	
	public void setKey_min(long keyMin) {
		key_min = keyMin;
	}
	
	public long getKey_max() {
		return key_max;
	}
	
	public void setKey_max(long keyMax) {
		key_max = keyMax;
	}

	public int getLevel() {
		return level;
	}

	public void setLevel(int level) {
		this.level = level;
	}


	public int getKey() {
		return key;
	}


	public void setKey(int key) {
		this.key = key;
	}


	public INode getLeft() {
		return left;
	}


	public void setLeft(INode left) {
		this.left = left;
	}


	public INode getRight() {
		return right;
	}


	public void setRight(INode right) {
		this.right = right;
	}


	public String getRecord() {
		return record;
	}


	public void setRecord(String record) {
		this.record = record;
	}


	public HashMap<Integer, String> getStore() {
		return store;
	}


	@Override
	public int getNodeNr() throws RemoteException {
		// TODO Auto-generated method stub
		return this.nodeNr;
	}

}
